﻿namespace MicroCloud.Utils
{
    /// <summary>
    /// 文件辅助操作类
    /// </summary>
    public class FileHelper
    {
        /// <summary>
        /// 获取文件流并关闭
        /// </summary>
        public static Stream GetFileStream(string path)
        {
            if (!File.Exists(path)) return null;
            using Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
            // 设置当前流的位置为流的开始
            stream.Seek(0, SeekOrigin.Begin);
            return stream;
        }

        /// <summary>
        /// 获取文件流字节数
        /// </summary>
        public static Byte[] GetFileBytes(string path)
        {
            if (!File.Exists(path)) return null;

            byte[] bytes = Array.Empty<byte>();
            using Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
            bytes = stream.ToBytes();
            return bytes;
        }

        /// <summary>
        /// 检测文件是否已被其它程序使用
        /// </summary>
        /// <param name="fileName">文件的完全限定名，例如：“C:\MyFile.txt”。</param>
        /// <returns>如果文件已被其它程序使用，则为 true；否则为 false。</returns>
        public static bool FileIsUsed(string fileName)
        {
            bool result = false;

            //判断文件是否存在，如果不存在，直接返回 false
            if (!File.Exists(fileName))
            {
                result = false;

            }//end: 如果文件不存在的处理逻辑
            else
            {//如果文件存在，则继续判断文件是否已被其它程序使用

                //逻辑：尝试执行打开文件的操作，如果文件已经被其它程序使用，则打开失败，抛出异常，根据此类异常可以判断文件是否已被其它程序使用。
                FileStream fileStream = null;
                try
                {
                    fileStream = File.Open(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);

                    result = false;
                }
                catch (IOException)
                {
                    result = true;
                }
                catch (System.Exception)
                {
                    result = true;
                }
                finally
                {
                    fileStream?.Close();
                }

            }//end: 如果文件存在的处理逻辑

            //返回指示文件是否已被其它程序使用的值
            return result;
        }

        /// <summary>
        /// 创建文件，如果文件不存在
        /// </summary>
        /// <param name="fileName">要创建的文件</param>
        public static void CreateIfNotExists(string fileName)
        {
            if (File.Exists(fileName))
            {
                return;
            }

            string dir = Path.GetDirectoryName(fileName);
            if (dir != null)
            {
                DirectoryHelper.CreateIfNotExists(dir);
            }
            File.Create(fileName);
        }

        /// <summary>
        /// 删除指定文件
        /// </summary>
        /// <param name="fileName">要删除的文件名</param>
        public static void DeleteIfExists(string fileName)
        {
            if (!File.Exists(fileName))
            {
                return;
            }
            File.Delete(fileName);
        }

        /// <summary>
        /// 设置或取消文件的指定<see cref="FileAttributes"/>属性
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <param name="attribute">要设置的文件属性</param>
        /// <param name="isSet">true为设置，false为取消</param>
        public static void SetAttribute(string fileName, FileAttributes attribute, bool isSet)
        {
            FileInfo fi = new(fileName);
            if (!fi.Exists)
            {
                //throw new FileNotFoundException("文件 {fileName} 不存在", fileName);
                throw new FileNotFoundException(I18N.T("文件 {0} 不存在", fileName));
            }
            if (isSet)
            {
                fi.Attributes = fi.Attributes | attribute;
            }
            else
            {
                fi.Attributes = fi.Attributes & ~attribute;
            }
        }

        /// <summary>
        /// 获取文件版本号
        /// </summary>
        /// <param name="fileName"> 完整文件名 </param>
        /// <returns> 文件版本号 </returns>
        public static string GetVersion(string fileName)
        {
            if (File.Exists(fileName))
            {
                FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(fileName);
                return fvi.FileVersion;
            }
            return null;
        }

        /// <summary>
        /// 获取文件的MD5值
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <returns>32位MD5</returns>
        public static string GetFileMD5(string fileName)
        {
            using FileStream fs = new(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
            const int bufferSize = 1024 * 1024;
            byte[] buffer = new byte[bufferSize];
            using MD5 md5 = MD5.Create();
            md5.Initialize();
            long offset = 0;
            while (offset < fs.Length)
            {
                long readSize = bufferSize;
                if (offset + readSize > fs.Length)
                {
                    readSize = fs.Length - offset;
                }
                fs.Read(buffer, 0, (int)readSize);
                if (offset + readSize < fs.Length)
                {
                    md5.TransformBlock(buffer, 0, (int)readSize, buffer, 0);
                }
                else
                {
                    md5.TransformFinalBlock(buffer, 0, (int)readSize);
                }
                offset += bufferSize;
            }
            fs.Close();
            byte[] result = md5.Hash;
            md5.Clear();
            StringBuilder sb = new(32);
            foreach (byte b in result)
            {
                sb.Append(b.ToString("X2"));
            }
            return sb.ToString();
        }

        /// <summary>
        /// 获取文本文件的编码方式
        /// </summary>
        /// <param name="fileName"> 文件名 例如：path = @"D:\test.txt"</param>
        /// <returns>返回编码方式</returns>
        public static Encoding GetEncoding(string fileName)
        {
            return GetEncoding(fileName, Encoding.Default);
        }

        /// <summary>
        /// 获取文本流的编码方式
        /// </summary>
        /// <param name="fs">文本流</param>
        /// <returns>返回系统默认的编码方式</returns>
        public static Encoding GetEncoding(FileStream fs)
        {
            //Encoding.Default 系统默认的编码方式
            return GetEncoding(fs, Encoding.Default);
        }

        /// <summary>
        /// 获取一个文本流的编码方式
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <param name="defaultEncoding">默认编码方式。当该方法无法从文件的头部取得有效的前导符时，将返回该编码方式。</param>
        /// <returns></returns>
        public static Encoding GetEncoding(string fileName, Encoding defaultEncoding)
        {
            using FileStream fs = File.Open(fileName, FileMode.Open);
            return GetEncoding(fs, defaultEncoding);
        }
   
        /// <summary>
        /// 获取一个文本流的编码方式
        /// </summary>
        /// <param name="fs">文本流</param>
        /// <param name="defaultEncoding">默认编码方式。当该方法无法从文件的头部取得有效的前导符时，将返回该编码方式。</param>
        /// <returns></returns>
        public static Encoding GetEncoding(FileStream fs, Encoding defaultEncoding)
        {
            Encoding targetEncoding = defaultEncoding;
            if (fs != null && fs.Length >= 2)
            {
                byte b1 = 0;
                byte b2 = 0;
                byte b3 = 0;
                byte b4 = 0;

                long oriPos = fs.Seek(0, SeekOrigin.Begin);
                fs.Seek(0, SeekOrigin.Begin);

                b1 = Convert.ToByte(fs.ReadByte());
                b2 = Convert.ToByte(fs.ReadByte());
                if (fs.Length > 2)
                {
                    b3 = Convert.ToByte(fs.ReadByte());
                }
                if (fs.Length > 3)
                {
                    b4 = Convert.ToByte(fs.ReadByte());
                }

                //根据文件流的前4个字节判断Encoding
                //Unicode {0xFF, 0xFE};
                //BE-Unicode {0xFE, 0xFF};
                //UTF8 = {0xEF, 0xBB, 0xBF};
                if (b1 == 0xFE && b2 == 0xFF)//UnicodeBe
                {
                    targetEncoding = Encoding.BigEndianUnicode;
                }
                if (b1 == 0xFF && b2 == 0xFE && b3 != 0xFF)//Unicode
                {
                    targetEncoding = Encoding.Unicode;
                }
                if (b1 == 0xEF && b2 == 0xBB && b3 == 0xBF)//UTF8
                {
                    targetEncoding = Encoding.UTF8;
                }

                fs.Seek(0, SeekOrigin.Begin);
            }
            return targetEncoding;
        }

        /// <summary>
        /// 保存文件并写入文件流
        /// </summary>
        /// <param name="stream">流</param>
        /// <param name="path">文件保存路径</param>
        /// <returns>文件流长度</returns>
        public static void Save(string path, Stream stream)
        {
            string dir = Path.GetDirectoryName(path);
            if (dir != null)
            {
                DirectoryHelper.CreateIfNotExists(dir);
            }

            stream.Seek(0, SeekOrigin.Begin);   //设置当前流的位置为流的开始
            const int FILE_WRITE_SIZE = 84975;  //写出缓冲区大小
            using (FileStream fileStream = new(path, FileMode.Create, FileAccess.Write, FileShare.Write, FILE_WRITE_SIZE, true))
            {
                byte[] byteArr = new byte[FILE_WRITE_SIZE];
                int readCount = 0;
                while ((readCount = stream.Read(byteArr, 0, byteArr.Length)) > 0)
                {
                    fileStream.Write(byteArr, 0, byteArr.Length);
                }
            }
            stream?.Close();
            stream?.Dispose();
        }

        /// <summary>
        /// 保存文件并写入文件流
        /// </summary>
        /// <param name="stream">流</param>
        /// <param name="path">文件保存路径</param>
        /// <returns>文件流长度</returns>
        public static async Task SaveAsync(string path, Stream stream)
        {
            string dir = Path.GetDirectoryName(path);
            if (dir != null)
            {
                DirectoryHelper.CreateIfNotExists(dir);
            }

            stream.Seek(0, SeekOrigin.Begin);   //设置当前流的位置为流的开始
            const int FILE_WRITE_SIZE = 84975;  //写出缓冲区大小
            using (FileStream fileStream = new(path, FileMode.Create, FileAccess.Write, FileShare.Write, FILE_WRITE_SIZE, true))
            {
                int readCount = 0;
                byte[] byteArr = new byte[FILE_WRITE_SIZE];
                while ((readCount = await stream.ReadAsync(byteArr.AsMemory(0, byteArr.Length))) > 0)
                {
                    await fileStream.WriteAsync(byteArr.AsMemory(0, readCount));
                }
            }
        }


        /// <summary>
        /// 删除指定文件
        /// </summary>
        /// <param name="path">要删除的文件名</param>
        public static void Delete(string path)
        {
            if (File.Exists(path))
            {
                File.Delete(path);
            }
        }

    }
}
